/*
 * Copyright (c) 2007 Compl Yue Still, All Rights Reserved.
 */

package oex.ui
{
	import flash.display.*;
	import flash.events.*;
	import flash.geom.*;
	import flash.ui.*;
	
	import mx.controls.*;
	import mx.controls.scrollClasses.*;
	import mx.core.*;
	import mx.events.*;
	
	public class FreeSpace extends UIComponent
	{
	
		public function FreeSpace()
		{
			this.addEventListener(FlexEvent.CREATION_COMPLETE, 
				onCreationComplete);

			this.addEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel);
			this.addEventListener(MouseEvent.MOUSE_DOWN, onMousePress);
			this.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
			this.addEventListener(MouseEvent.MOUSE_UP, onMouseRelease);
		}
		
		private var _plate:FreePlate;
		
		private var _hScroll:HScrollBar;
		
		private var _vScroll:VScrollBar;
		
		private function onCreationComplete(e:Event):void
		{
			if(e.target != this) return;

			var wView:int = unscaledWidth-_vScroll.minWidth;
			var hView:int = unscaledHeight-_hScroll.minHeight;
			
			var ter:EdgeMetrics = getTerritoryBounds();
			
			_hScroll.maxScrollPosition = ter.right - ter.left - wView;
			_vScroll.maxScrollPosition = ter.bottom - ter.top - hView;
			
			scrollToOrigin();
		}
		
		protected function populateContextMenu(cm:ContextMenu):void
		{
			var mi:ContextMenuItem;
			
			mi = new ContextMenuItem("Center View");
			mi.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,
				function (event:ContextMenuEvent):void
			{
				FreeSpace(event.contextMenuOwner).scrollToOrigin();
			});
			cm.customItems.push(mi);
		}
		
		override protected function createChildren():void
		{
			super.createChildren();
			
			if(!_plate)
			{
				var plate:FreePlate = _plate = new FreePlate();
				plate.addEventListener(FreePlate.TERRITORY_CHANGE, 
					onTerritoryChange);
				super.addChild(plate);
			}

			if(!_hScroll)
			{
				var hScroll:HScrollBar = _hScroll = new HScrollBar();
				hScroll.lineScrollSize = 16;
				hScroll.pageScrollSize = 80;
				hScroll.addEventListener(ScrollEvent.SCROLL, onScroll);
				super.addChild(hScroll);

			}

			if(!_vScroll)
			{
				var vScroll:VScrollBar = _vScroll = new VScrollBar();
				vScroll.lineScrollSize = 16;
				vScroll.pageScrollSize = 80;
				vScroll.addEventListener(ScrollEvent.SCROLL, onScroll);
				super.addChild(vScroll);
			}
			
			if(!this.contextMenu)
			{
				var cm:ContextMenu = new ContextMenu();
				cm.hideBuiltInItems();

				this.populateContextMenu(cm);
				
				this.contextMenu = cm;
			}
		}
		
		override protected function updateDisplayList
			(unscaledWidth:Number, unscaledHeight:Number):void
		{
			super.updateDisplayList(unscaledWidth, unscaledHeight);
			
			var wView:int = unscaledWidth-_vScroll.minWidth;
			var hView:int = unscaledHeight-_hScroll.minHeight;
			
			this.graphics.clear();
			this.graphics.lineStyle(0,0,0);
			this.graphics.beginFill(0,0);
			this.graphics.drawRect(0,0,wView,hView);
			
			var ter:EdgeMetrics = updateTerritoryBounds();
			
			_hScroll.pageSize = wView;
			_hScroll.setActualSize(wView, _hScroll.minHeight);
			_hScroll.move(0, hView);

			_vScroll.pageSize = hView;
			_vScroll.setActualSize(_vScroll.minWidth, unscaledHeight);
			_vScroll.move(wView, 0);

			scrollPlate(ter);
		}
		
		public function addShape(shape:ObjectShape):ObjectShape
		{
			shape.addEventListener(MoveEvent.MOVE, onComponentChange);
			shape.addEventListener(ResizeEvent.RESIZE, onComponentChange);
			return plate.addChild(shape) as ObjectShape;
		}
		
		public function removeShape(shape:ObjectShape):void
		{
			shape.parent.removeChild(shape);
		}

		public function shapeMoved(shape:ObjectShape):void
		{
		}
		public function shapeScaled(shape:ObjectShape):void
		{
		}
		public function shapeRotated(shape:ObjectShape):void
		{
		}

		public function currentCenterPoint():Point
		{
			var x:int = unscaledWidth/2 - _plate.x;
			var y:int = unscaledHeight/2 - _plate.y;

			return new Point(x, y);
		}
		
		public function scrollToOrigin():void
		{
			var wView:int = unscaledWidth-_vScroll.minWidth;
			var hView:int = unscaledHeight-_hScroll.minHeight;
			
			var ter:EdgeMetrics = getTerritoryBounds();
			
			_hScroll.scrollPosition = - ter.left - (wView/2);
			_vScroll.scrollPosition = - ter.top - (hView/2);
			
			scrollPlate(ter);

			_hScroll.invalidateDisplayList(); _hScroll.validateNow();
			_vScroll.invalidateDisplayList(); _vScroll.validateNow();
		}
		
		private function onComponentChange(e:Event):void
		{
			updateScrollRanges();
		}
		
		private function onTerritoryChange(e:Event):void
		{
			updateScrollRanges();
		}
		
		private function updateScrollRanges():void
		{
			var wView:int = unscaledWidth-_vScroll.minWidth;
			var hView:int = unscaledHeight-_hScroll.minHeight;
			
			var ter:EdgeMetrics = updateTerritoryBounds();
			
			var hMax:int = ter.right - ter.left - wView;
			var vMax:int = ter.bottom - ter.top - hView;
			
			if(_hScroll.scrollPosition > _hScroll.maxScrollPosition/2)
				_hScroll.scrollPosition += hMax - _hScroll.maxScrollPosition;
			if(_vScroll.scrollPosition > _vScroll.maxScrollPosition/2)
				_vScroll.scrollPosition += vMax - _vScroll.maxScrollPosition;

			_hScroll.maxScrollPosition = hMax;
			_vScroll.maxScrollPosition = vMax;
			
			scrollPlate(ter);

			_hScroll.invalidateDisplayList(); _hScroll.validateNow();
			_vScroll.invalidateDisplayList(); _vScroll.validateNow();
		}
		
		private function onMouseWheel(e:MouseEvent):void
		{
            var bar:ScrollBar = e.shiftKey ? _hScroll : _vScroll;
            var delta:int = - e.delta;
            e.stopPropagation();
            
            if(delta < 0)
            {
            	if(delta > - bar.lineScrollSize)
            		delta = - bar.lineScrollSize;
            }
            else
            {
            	if(delta < bar.lineScrollSize)
            		delta = bar.lineScrollSize;
            }
            
            var oldPosition:int = bar.scrollPosition;
            bar.scrollPosition += delta+delta+delta;
            bar.invalidateDisplayList(); bar.validateNow();

            var scrollEvent:ScrollEvent = new ScrollEvent(ScrollEvent.SCROLL);
            scrollEvent.direction = ScrollEventDirection.VERTICAL;
            scrollEvent.position = bar.scrollPosition;
            scrollEvent.delta = bar.scrollPosition - oldPosition;
            bar.dispatchEvent(scrollEvent);
		}
		
		private function onScroll(e:ScrollEvent):void
		{
			scrollPlate(getTerritoryBounds());
		}
		
		private function scrollPlate(ter:EdgeMetrics):void
		{
			var x:int = - ter.left - _hScroll.scrollPosition;
			var y:int = - ter.top - _vScroll.scrollPosition;
			_plate.x = x;
			_plate.y = y;
		}
				
		private var territory:EdgeMetrics = null;

		private function getTerritoryBounds():EdgeMetrics
		{
			if(territory == null) 
				return territory = calculateTerritoryBounds();
			else
				return territory;
		}
		
		private function updateTerritoryBounds():EdgeMetrics
		{
			return territory = calculateTerritoryBounds();
		}
		
		private function calculateTerritoryBounds():EdgeMetrics
		{
			const MARGIN:int = 200;
			
			var wView:int = unscaledWidth-_vScroll.minWidth;
			var hView:int = unscaledHeight-_hScroll.minHeight;
			var halfWidth:int = wView/2;
			var halfHeight:int = hView/2;
			
			var ter:EdgeMetrics = _plate.getTerritoryBounds();
			
			if(ter.left > -halfWidth-MARGIN) ter.left = -halfWidth-MARGIN;
			if(ter.right < halfWidth+MARGIN) ter.right = halfWidth+MARGIN;
			if(ter.top > -halfHeight-MARGIN) ter.top = -halfHeight-MARGIN;
			if(ter.bottom < halfHeight+MARGIN) ter.bottom = halfHeight+MARGIN;

			return ter;			
		}
		
		private var dragInitPlate:Point = null;
		private var dragInitMouse:Point = null;
		
		private function onMousePress(e:MouseEvent):void
		{
			if(e.target == this) 
			{
				dragInitPlate = new Point(_plate.x, _plate.y);
				dragInitMouse = new Point(e.stageX, e.stageY);
			}
		}
		
		private function onMouseMove(e:MouseEvent):void
		{
			if(dragInitPlate == null || dragInitMouse == null) return;
			
			var wView:int = unscaledWidth-_vScroll.minWidth;
			var hView:int = unscaledHeight-_hScroll.minHeight;
			
			var ter:EdgeMetrics = getTerritoryBounds();
			
			var minX:int = wView - ter.right;
			var maxX:int = - ter.left;
			var minY:int = hView - ter.bottom;
			var maxY:int = - ter.top;
			
			var x:int = dragInitPlate.x + e.stageX - dragInitMouse.x;
			var y:int = dragInitPlate.y + e.stageY - dragInitMouse.y;
			
			if(x < minX) x = minX;
			if(x > maxX) x = maxX;
			if(y < minY) y = minY;
			if(y > maxY) y = maxY;
			
			_plate.x = x; _plate.y = y;
			
			_hScroll.scrollPosition = - x - ter.left;
			_vScroll.scrollPosition = - y - ter.top;

			_hScroll.invalidateDisplayList(); _hScroll.validateNow();
			_vScroll.invalidateDisplayList(); _vScroll.validateNow();
		}
		
		private function onMouseRelease(e:MouseEvent):void
		{
			dragInitPlate = null;
			dragInitMouse = null;
		}
		
		public function get plate():UIComponent
		{
			return _plate;
		}
		
	}
}
